/*
//
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2007 Intel Corporation. All Rights Reserved.
//
//
*/

#include "umc_defs.h"
#if defined(UMC_ENABLE_AVS_VIDEO_DECODER)

#include "umc_avs_dec_fusion_core.h"

#include "umc_avs_pic.h"
#include "umc_avs_dec_processing_unit_cc.h"
#include "umc_avs_dec_processing_unit_dec.h"
#include "umc_avs_dec_processing_unit_rec.h"
#include "umc_avs_dec_processing_unit_decrec.h"
#include "umc_avs_dec_processing_unit_deb.h"
#include "umc_avs_processing_unit_sleep.h"

#include "umc_video_decoder.h"
#include "umc_automatic_mutex.h"

namespace UMC
{

enum
{
    AVS_DEF_NUMBER_OF_FRAMES    = 2
};

AVSDecFusionCore::AVSDecFusionCore(void)
{
    m_prevSliceStartCode = 0;

} // AVSDecFusionCore::AVSDecFusionCore(void)

AVSDecFusionCore::~AVSDecFusionCore(void)
{
    Release();

} // AVSDecFusionCore::~AVSDecFusionCore(void)

Status AVSDecFusionCore::Release(void)
{
    // reset the variables
    m_prevSliceStartCode = 0;

    // call the parent's method
    AVSFusionCore::Release();

    return UMC_OK;

} // Status AVSDecFusionCore::Close(void)

#define AVS_ALLOCATE_PROCESSING_UNIT(name) \
    { \
        AVSListElement<AVSProcessingUnit> *pTemp; \
        pTemp = new name(); \
        if (NULL == pTemp) \
            return UMC_ERR_ALLOC; \
        pTemp->m_threadNumber = i; \
        m_pPerformers[i].AddToTail(*pTemp); \
        umcRes = pTemp->Init(&m_guard); \
        if (UMC_OK != umcRes) \
            return UMC_ERR_FAILED; \
    }

Status AVSDecFusionCore::Init(Ipp32u numThreads, BaseCodecParams *pParams)
{
    VideoDecoderParams *pVParams = DynamicCast<VideoDecoderParams> (pParams);
    AVSListElement<AVSProcessingUnit> *pPerf;
    Status umcRes;
    Ipp32u i;

    // check error(s)
    if (NULL == pVParams)
        return UMC_ERR_NULL_PTR;

    // release the object before initialization
    Release();

    // call the parent's method
    umcRes = AVSFusionCore::Init(numThreads, pParams);
    if (UMC_OK != umcRes)
        return umcRes;

    // allocate one more frame to hide stalls,
    // when the current frame is ready to show,
    // but main thread is working.
    m_maxFrames = AVS_DEF_NUMBER_OF_FRAMES + ((1 == numThreads) ? (1) : (numThreads + 1));

    // allocates the specific performers
    pPerf = new AVSColorProcessingUnit();
    if (NULL == pPerf)
        return UMC_ERR_ALLOC;
    m_pPerformers[0].AddToTail(*pPerf);
    umcRes = ((AVSColorProcessingUnit *) pPerf)->Init(&m_guard, pVParams->pPostProcessing);
    if (UMC_OK != umcRes)
        return UMC_ERR_FAILED;

    for (i = 0; i < numThreads; i += 1)
    {
        // allocate the deblocker
        AVS_ALLOCATE_PROCESSING_UNIT(AVSDebProcessingUnit);

        // allocate the decoder(s)
        if (1 == numThreads)
        {
            AVS_ALLOCATE_PROCESSING_UNIT(AVSDecRecProcessingUnit);
        }
        else
        {
            AVS_ALLOCATE_PROCESSING_UNIT(AVSRecProcessingUnit);

            AVS_ALLOCATE_PROCESSING_UNIT(AVSDecProcessingUnit);
        }

        // allocate the sleeper
        AVS_ALLOCATE_PROCESSING_UNIT(AVSSleepProcessingUnit);
    }

    return UMC_OK;

} // Status AVSDecFusionCore::Init(Ipp32s iNumThreads, VideoDecoderParams *pParams)

Status AVSDecFusionCore::SetDestination(MediaData *pDst)
{
    VideoData *pVDst = DynamicCast<VideoData> (pDst);
    AVSListElement<AVSFrame> *pFrame, *pPrevRef = NULL, *pToShow = NULL;

    // check error(s)
    if (NULL == pVDst)
        return UMC_ERR_NULL_PTR;

    // we can touch frame queue without the synchronization,
    // because we not intend to modify it.
    pFrame = m_Frames.GetHead();
    while (pFrame)
    {
        if (false == pFrame->IsShown())
        {
            if (pFrame->IsReference())
            {
                // first reference frame should be shown first
                if (NULL == pPrevRef)
                {
                    pToShow = pFrame;
                    break;
                }
                else if (false == pPrevRef->IsShown())
                {
                    pToShow = pPrevRef;
                    break;
                }
            }
            else
            {
                // any non-reference frame can be shown
                pToShow = pFrame;
                break;
            }
        }

        // save the previous reference frame
        if (pFrame->IsReference())
             pPrevRef = pFrame;

        // get the next frame
        pFrame = pFrame->GetNext();
    }
    // show the last reference
    if ((NULL == pFrame) &&
        (pPrevRef) &&
        (false == pPrevRef->IsShown()))
    {
        pToShow = pPrevRef;
    }
    if (NULL == pToShow)
        return UMC_ERR_NOT_ENOUGH_DATA;

    // save the conversion parameters
    pToShow->m_cnvCtx.m_pPicDst = pVDst;

    return UMC_OK;

} // Status AVSDecFusionCore::SetDestination(VideoData *pDst)

} // namespace UMC

#endif // defined(UMC_ENABLE_AVS_VIDEO_DECODER)
